权限组件
只要继承了 APIView 就可以使用权限组件
组件的执行顺序: 认证 -> 权限 -> 频率
1. 权限类的创建
- 权限类的固定写法一 -> 没有继承 BasePermission 权限类
- 如果没有继承 BasePermission 权限类,那么就需要在权限类中编写两个方法
- has_permission -> 编写权限的逻辑代码
- has_object_permission -> 不知道有啥用,反正就是要写上 rest-framework 的源码规定的,否则就会报错
- 注意:
- has_permission 方法必须返回True(也可以是能代表True的元素,即: 字符串、列表、字典等)或则False(也可以是能代表True的元素,即: 0、None等),因为是 rest-framework 的源码规定的
# rf_permission.py
class 权限类的类名(object):
message = '错误信息' # 配置错误信息,如果不配置会使用默认的错误信息
def has_permission(self, request, view):
# 权限的逻辑代码
if True / False:
return True
else:
return False
def has_object_permission(self, request, view, obj):
return True
- 权限类的固定写法二 -> 继承 BasePermission 权限类 -> 常用
- BasePermission 权限类其实就是一个初始化好的权限类
- BasePermission 权限类里面也有 has_permission 和 has_object_permission 方法,只不过这两个方法里面没有任何逻辑代码
- 在编写权限类的时候可以继承 BasePermission 权限类,然后重写 has_permission 方法,而自己编写的权限类就无需写 has_object_permission 方法了,因为 BasePermission 权限类里面有
- 注意:
- has_permission 方法必须返回True(也可以是能代表True的元素,即: 字符串、列表、字典等)或则False(也可以是能代表True的元素,即: 0、None等),因为是 rest-framework 的源码规定的
# rf_permission.py
from rest_framework.permissions import BasePermission
class 权限类的类名(BasePermission):
message = '错误信息' # 配置错误信息,如果不配置会使用默认的错误信息
def has_permission(self, request, view):
# 权限的逻辑代码
if True / False:
return True
else:
return False
- 权限类的例子
# rf_permission.py
from rest_framework.permissions import BasePermission
class SVIPPermission(BasePermission):
message = "SVIP才能访问!" # 配置错误信息,如果不配置会使用默认的错误信息
def has_permission(self, request, view):
"""
:param request: APIView 的 request
:param view: 视图类的实例化对象 -> APIView 类中的 self
:return: True/False
"""
# request.user / request.auth 可以使用是因为先执行了认证类然后再执行权限类,且在执行认证类的authenticate方法的时候已经把返回值分别赋值给 request.user / request.auth
if request.user.user_type == 3:
print(request.user) # 用户的数据对象
print(request.auth) # token: 7a06fa70c1516f8cd9399e17285d9903
return True
else:
return False
2. 局部权限
- 局部权限的配置(即: permission_classes = [权限类, 权限类])会覆盖掉该视图类在全局权限中的配置
- permission_classes= [权限类, 权限类]
# rf_permission.py
from rest_framework.permissions import BasePermission
class SVIPPermission(BasePermission):
message = "SVIP才能访问!" # 配置错误信息,如果不配置会使用默认的错误信息
def has_permission(self, request, view):
"""
:param request: APIView 的 request
:param view: 视图类的实例化对象 -> APIView 类中的 self
:return: True/False
"""
# request.user / request.auth 可以使用是因为先执行了认证类然后再执行权限类,且在执行认证类的authenticate方法的时候已经把返回值分别赋值给 request.user / request.auth
if request.user.user_type == 3:
print(request.user) # 用户的数据对象
print(request.auth) # token: 7a06fa70c1516f8cd9399e17285d9903
return True
else:
return False
# views.py
from .models import *
from .serializer import *
from .rf_auth import *
from .rf_permission import *
from rest_framework import viewsets
class BookViewSet(viewsets.ModelViewSet):
authentication_classes = [TokenAuth] # 局部认证,只作用于当前视图
permission_classes = [SVIPPermission] # 局部权限,只作用于当前视图
queryset = Book.objects.all()
serializer_class = BookSerializers
3. 全局权限
- 全局权限作用于所有视图类
# rf_permission.py
from rest_framework.permissions import BasePermission
class SVIPPermission(BasePermission):
message = "SVIP才能访问!" # 配置错误信息,如果不配置会使用默认的错误信息
def has_permission(self, request, view):
"""
:param request: APIView 的 request
:param view: 视图类的实例化对象 -> APIView 类中的 self
:return: True/False
"""
# request.user / request.auth 可以使用是因为先执行了认证类然后再执行权限类,且在执行认证类的authenticate方法的时候已经把返回值分别赋值给 request.user / request.auth
if request.user.user_type == 3:
print(request.user) # 用户的数据对象
print(request.auth) # token: 7a06fa70c1516f8cd9399e17285d9903
return True
else:
return False
# settings.py
# 配置全局权限所需要的权限类的路径
REST_FRAMEWORK = {
# "DEFAULT_AUTHENTICATION_CLASSES": ["app01.rf_auth.TokenAuth"], # 全局认证
"DEFAULT_PERMISSION_CLASSES": ["app01.rf_permission.SVIPPermission"] # 全局权限
}
- 不进行权限认证的视图类设置
- 如果设置了全局权限,但是又想某些视图不进行权限认证,那么可以在不想进行权限认证的视图类中设置 permission_classes= [],因为在 rest-framework 源码中 局部权限的配置 会覆盖掉 全局权限的配置
# views.py
import hashlib
import time
from django.shortcuts import render, HttpResponse
from rest_framework.views import APIView
from rest_framework.response import Response
from .models import *
from .serializer import *
from .rf_auth import *
from rest_framework import viewsets
class BookViewSet(viewsets.ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializers
class PublishViewSet(viewsets.ModelViewSet):
queryset = Publish.objects.all()
serializer_class = PublishSerializers
# 获取以用户名作为盐,以当前时间作为加密内容的字符串token
def get_token_str(username):
now_time = str(time.time())
md5 = hashlib.md5(bytes(username, encoding="utf8")) # 加盐,使用用户名作为盐,保证token的唯一性
md5.update(bytes(now_time, encoding='utf-8'))
return md5.hexdigest()
# 登陆,并且返回当前用户的token
class LoginView(APIView):
permission_classes = [] # 在全局权限下,当前视图不进行权限认证
def post(self, request):
username = request.data.get('username')
password = request.data.get('password')
user = User.objects.filter(username=username, password=password).first()
ret = {
'status': 0,
'msg': None
}
if user:
token_str = get_token_str(user.username) # 获取Token值
# 每次登陆成功后都要修改当前用户的token值
Token.objects.update_or_create(user=user, defaults={'token': token_str}) # 如果有 user=user 这条数据,那么就修改 token 值,否则就进行添加
ret['status'] = 1
ret['token'] = token_str
else:
ret['msg'] = '登陆失败'
return Response(ret)